/*********************************************************************************
 *      Copyright:  (C) 2025 iot25<lingyun@email.com>
 *                  All rights reserved.
 *
 *       Filename:  socket.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(13/05/25)
 *         Author:  iot25 <lingyun@email.com>
 *      ChangeLog:  1, Release initial version on "13/05/25 18:26:55"
 *                 
 ********************************************************************************/
#include <time.h>
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "socket.h"
#include "sqlite.h"
#include "logger.h"

#define CONNECT_RETRY_TIMES 3
#define CONNECT_RETRY_DELAY_MS 200

//初始化socket 
int  socket_init(socket_t *sock,char *hostname, int port)
{
    int 		status = -1;
    if ( (!sock) ||  !hostname || (port < 0) )
    {
        log_error("Invalid arguments: sock=%p, hostname=%p, port=%d\n", 
                 sock, hostname, port);
        return -1;
    }

    memset(sock, 0, sizeof(socket_t));

    strncpy(sock->host, hostname, sizeof(sock->host)-1);//服务器主机名ip 
    sock->port = port;    //服务器端口
    sock->fd = -1;
    sock->saved_flags = 0;
    
	log_info("Socket initialized: host=%s, port=%d\n", sock->host, sock->port);
    return 0;
}

// 获取地址信息
int get_address_info(const char *host, const char *port, struct addrinfo **res)
{
    struct 			addrinfo hints;
    int 			status;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    status = getaddrinfo(host, port, &hints, res);
    if (status != 0) 
    {
        log_error("getaddrinfo [%s:%s] failed: %s\n", host, port, gai_strerror(status));
        return -1;
    }
    return 0;
}

//socket连接
int  socket_connect(socket_t *sock)
{
    char 				port_str[16] = {0};
    int 				retries = 0;
    int 				status = -1;
    struct addrinfo 	hints;
    struct addrinfo 	*res = NULL;
    struct addrinfo 	*ip_buf = NULL;
    int 				flags;
    struct sockaddr_in 	*addr;
    char ip_str[INET_ADDRSTRLEN] = {0};
    fd_set 				writefds;
    struct timeval 		tv;
    int 				select_ret;
    int 				so_error;
    socklen_t len;

    if (!sock || sock->port <= 0)
    {
        log_error("Invallid socket paramerers\n");
        return -1;
    }
    // 确保初始状态
    if (sock->fd >= 0)
    {
        close(sock->fd);
        sock->fd = -1;
    }
    sock->connected = 0;

    snprintf(port_str, sizeof(port_str), "%d", sock->port);

    memset(&hints, 0, sizeof(hints));

    while (retries < CONNECT_RETRY_TIMES)
    {
        log_debug("Connection attempt %d/%d", retries + 1, CONNECT_RETRY_TIMES);
        if (get_address_info(sock->host, port_str, &res) != 0) 
        {
            goto cleanup;
        }

        // 遍历所有可能的地址
        for (ip_buf = res; ip_buf != NULL; ip_buf = ip_buf->ai_next)
        {
            // 确保前一次尝试的 fd 已关闭
            if (sock->fd >= 0)
            {
                close(sock->fd);
                sock->fd = -1;
            }

            // 创建Socket（IPv4）
            sock->fd = socket(AF_INET, SOCK_STREAM, 0);
            if (sock->fd < 0) 
            {
                log_error("socket() failed: %s", strerror(errno));
                continue;
            }

            // 设置为非阻塞模式并保存原始Flags
            flags = fcntl(sock->fd, F_GETFL);
            if (flags < 0 || fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK) < 0) 
            {
                log_error("fcntl() failed: %s", strerror(errno));
                close(sock->fd);
                sock->fd = -1;
                continue;
            }
            sock->saved_flags = flags;

            // 转换为 IPv4 地址字符串
            addr = (struct sockaddr_in*)ip_buf->ai_addr;
            inet_ntop(AF_INET, &addr->sin_addr, ip_str, sizeof(ip_str));
            log_debug("Trying IPv4 address: %s:%d", ip_str, sock->port);

            // 尝试连接
            status = connect(sock->fd, (struct sockaddr*)addr, sizeof(struct sockaddr_in));
            if (status == 0)
            {
                log_debug("Immediate IPv4 connection success");
                goto connection_success;
            }

            if (errno != EINPROGRESS) 
            {
                log_warn("Connect failed immediately: %s\n", strerror(errno));
                close(sock->fd);
                sock->fd = -1;
                continue;
            }

            // 等待连接完成
            FD_ZERO(&writefds);
            FD_SET(sock->fd, &writefds);
            tv.tv_sec = 3;
            tv.tv_usec = 0;

            select_ret = select(sock->fd+1, NULL, &writefds, NULL, &tv);

            if (select_ret <= 0)
            {
                log_warn("Connect timeout or error: %s\n",select_ret == 0 ? "timeout" : strerror(errno));
                close(sock->fd);
                sock->fd = -1;
                continue;
            }

            // 检查连接状态
            len = sizeof(so_error);
            if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0 ||
                so_error != 0) 
            {
                log_warn("Connection failed: %s\n",so_error ? strerror(so_error) : "getsockopt failed");
                close(sock->fd);
                sock->fd = -1;
                continue;
            }

            log_debug("IPv4 connection success after select\n");
            goto connection_success;
        }

        // 本次尝试失败，准备重试
        freeaddrinfo(res);
        res = NULL;

        retries++;
        if (retries < CONNECT_RETRY_TIMES)
        {
            log_error("Connection attempt %d/%d failed, retrying in %dms...\n",retries, 		CONNECT_RETRY_TIMES, CONNECT_RETRY_DELAY_MS);
            usleep(CONNECT_RETRY_DELAY_MS * 1000);
        }
    }

    log_error("Failed to connect to %s:%d after %d attempts\n",sock->host, sock->port, CONNECT_RETRY_TIMES);
    goto cleanup;

connection_success:
    // 恢复阻塞模式
    if (fcntl(sock->fd, F_SETFL, sock->saved_flags) < 0)
    {
        log_error("Failed to restore socket flags: %s", strerror(errno));
    }
    sock->connected = 1;

    // 仅在 res 未释放时调用 freeaddrinfo
    if (res != NULL)
    {
        freeaddrinfo(res);
        res = NULL;  // 防止后续误用
    }
    return 0;

cleanup:
    if (res != NULL)
    {
        freeaddrinfo(res);
        res = NULL;
    }
    if (sock->fd >= 0)
    {
        close(sock->fd);
        sock->fd = -1;
    }
    sock->connected = 0;
    return -1;
}

//判断和设置socket连接标志 
int socket_diag(socket_t *sock)
{
    int error = 0;
    socklen_t len = sizeof(error);
    fd_set writefds;
    struct timeval tv;
    int select_ret;

    if (!sock || sock->fd < 0)
    {
        log_error("Invalid socket or fd: sock=%p, fd=%d", sock, sock ? sock->fd : -2);
        return -1;
    }

    if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
    {
        log_error("getsockopt failed: %s\n", strerror(errno));
        sock->connected = 0;
        return -1;
    }

    if (error != 0)
    {
        log_warn("Socket error: %s\n", strerror(error));
        sock->connected = 0;
        return 0;
    }

    // 额外检查是否真的可写
    tv.tv_sec = 0;
    tv.tv_usec = 1000;
    FD_ZERO(&writefds);
    FD_SET(sock->fd, &writefds);

    select_ret = select(sock->fd+1, NULL, &writefds, NULL, &tv);
    if (select_ret <= 0)
    {
        sock->connected = 0;
        return 0;
    }

    sock->connected = 1;
    return 1;
}

//写入数据 
int socket_write(socket_t *sock, char*data, int bytes)
{
    int total_sent = 0;
    fd_set writefds;
    struct timeval tv;
    int remaining;
    int chunk;
    int select_ret;
    int wv;

    if (!sock || sock->fd < 0 || !data || bytes <= 0)
    {
        log_error("Invalid arguments: sock=%p fd=%d data=%p bytes=%d\n",
                 sock, sock ? sock->fd : -1, data, bytes);
        return -1;
    }

    log_debug("即将发送的数据: %s\n", data); // 添加调试信息，打印即将发送的数据

    while (total_sent < bytes)
    {
        remaining = bytes - total_sent;
        chunk = remaining > 1024 ? 1024 : remaining;

        tv.tv_sec = 5;
        tv.tv_usec = 0;

        FD_ZERO(&writefds);
        FD_SET(sock->fd, &writefds);

        select_ret = select(sock->fd + 1, NULL, &writefds, NULL, &tv);
        if (select_ret <= 0)
        {
            log_error("Write timeout or error: %s\n",
                    select_ret == 0 ? "timeout" : strerror(errno));
            return -1;
        }

        wv = write(sock->fd, data + total_sent, chunk);
        if (wv <= 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                usleep(10000); // 10ms delay
                continue;
            }

            log_error("Write failed: %s (fd=%d, sent=%d/%d)\n",
                     strerror(errno), sock->fd, total_sent, bytes);
            return -1;
        }
        total_sent += wv;
    }
    log_debug("数据发送成功，共发送了 %d 字节\n", total_sent); // 添加调试信息，打印发送结果
    return total_sent;
}

/* 读取数据 */
int socket_read(socket_t *sock, char *data, int bytes)
{
    int rv = 0;

    if ( (NULL == sock) || (NULL == data) )
    {
        log_error("NULL pointer");
        return -1;
    }

    rv = read(sock->fd, data, bytes);
    if ( rv <= 0 )
    {
        log_error("read failure: %s\n", strerror(errno));
        return 0;
    }
    return 1;
}

//关闭socket
int socket_close(socket_t *sock)
{
    if ( NULL == sock )
    {
        log_error("NULL pointer\n");
        return 0;
    }
    if (sock->fd >= 0)
    {
        close(sock->fd);
        sock->fd = -1;
    }
    return 1;
}

